﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using WebAPI_Demo.Models;
using WebAPI_Demo.Models.Database;

namespace WebAPI_Demo.Controllers
{
    [Route("api/[controller]/[action]")]
    [ApiController]
    public class CacheController : ControllerBase
    {
        private readonly IMemoryCache cache;
        private readonly Store_2022Context _db;
        /// <summary>
        /// 构造注入
        /// </summary>
        /// <param name="cache"></param>
        public CacheController(IMemoryCache cache, Store_2022Context db)
        {
            this.cache = cache;
            _db = db;
        }

        // KEY Value

        /// <summary>
        /// 判断缓存是否存在
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> TryGetValueCache()
        {
            //cache.TryGetValue("KEY键名")
            //out 关键字 引用传递
            //变量传递到方法里 在方法里修改 该变量不会进行修改 out ref _指把变量放弃掉了
            object obj;
            //TryGetValue
            //判断有没有ke'y的值存在 如果不存在方法缓存false
            //如果存在方法返回true 同时把值value传递到obj
            if (cache.TryGetValue("UserName", out obj))
            {
                //obj缓存值的内容
                return "该缓存存在";
            }
            else
            {
                return "该缓存不存在";
            }
        }
        [HttpGet]
        public int B()
        {
            int a = 5; //B执行域内存定义a
            //值传递
            //把a的值（是一个值）传进去A方法 
            A(a);
            //a==? 1??? a==5

            //引用传递
            //out使值传递 变成 引用传递
            //引用传递 传递时a的指针 内存里的地址
            C(out a);

            //a==??
            return a;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        [NonAction]
        public void A(int a) //a=5 在A方法的执行域 定义了一个a变量
        {
            //赋值1
            a = 1;
        }

        [NonAction]
        public void C(out int a)
        {
            a = 2;
        }

        /// <summary>
        /// 读写缓存
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> GetCache()
        {
            // 写入缓存
            //cache.Set("C6","1")
            cache.Set("UserName", "liu");
            cache.Set("Password", "12345");


            // 读取缓存
            //cache.Get<string>("C6")
            //cache.Get<缓存的数值类型>("key")
            string userName = cache.Get<string>("UserName");
            string password = cache.Get<string>("Password");

            // 返回
            return userName + "\n" + password;
        }

        /// <summary>
        /// 读写缓存
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> GetOrCreateCache()
        {
            //如果一个缓存值存在，则返回它，如果不存在，则创建该缓存后再返回它。
            //cache.GetOrCreate("UserName")有缓存数据 则 直接缓存

            //                                              语法 委托（把方法 当作参数传递执行）
            //如果UserName的缓存存在 则返回  如果不存在则用"admin"的值保存缓存 然后再返回
            string userName = cache.GetOrCreate("UserName", entry =>
            {
                //返回了一个字符串
                return "admin";
            });

            string password = cache.GetOrCreate("Password", entry =>
            {
                return "12345";
            });
            return userName + "\n" + password;
        }

        /// <summary>
        /// 删除缓存
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> RemoveCache()
        {
            // 写入缓存
            cache.Set("UserName", "admin");
            cache.Set("Password", "12345");

            // 删除缓存
            cache.Remove("UserName");
            cache.Remove("Password");

            // 返回
            return "缓存删除成功";
        }

        /// <summary>
        /// 绝对时间过期
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> CacheTime2()
        {
            string msg = string.Empty;
            //set(key , value , 过期时间参数)
            //创建缓存多少时间后过期
            cache.Set("UserName", "admin", TimeSpan.FromSeconds(3));

            //测试代码
            // 读取缓存
            for (int i = 1; i <= 5; i++)
            {
                msg += $"第{i}秒缓存值：{cache.Get<string>("UserName")}\n";
                //让线程睡眠 睡眠了（1000毫秒）一秒
                Thread.Sleep(1000);
            }

            // 返回
            return msg;
        }

        /// <summary>
        /// 滑动时间过期
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> CacheTime3()
        {
            string msg = string.Empty;
            //如果被命中则延续时间

            //滑动时间
            //3秒内 如果有请求
            //则续上3秒缓存时间

            //否则 删除缓存

            cache.Set("UserName", "admin", new MemoryCacheEntryOptions
            {
                SlidingExpiration = TimeSpan.FromSeconds(3)
            });

            // 读取缓存
            for (int i = 1; i <= 5; i++)
            {
                msg += $"第{i}秒缓存值：{cache.Get<string>("UserName")}\n";
                Thread.Sleep(1000);
            }

            // 返回
            return msg;
        }

        /// <summary>
        /// 绝对时间过期+滑动时间过期
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        public ActionResult<string> Get()
        {
            string msg = string.Empty;
            //两个时间策略同时启用 | 任意一个失效 另外一个也失效
            cache.Set("UserName", "admin", new MemoryCacheEntryOptions
            {
                //滑动 1.5内有访问则 延续1.5
                SlidingExpiration = TimeSpan.FromSeconds(1.5),
                //3秒后必然失效
                AbsoluteExpiration = DateTimeOffset.Now.AddSeconds(3)
            });

            // 读取缓存
            for (int i = 1; i <= 5; i++)
            {
                msg += $"第{i}秒缓存值：{cache.Get<string>("UserName")}\n";
                Thread.Sleep(1000);
            }

            // 返回
            return msg;
        }

        //编写接口获取商品列表数据，并且使用本地缓存保存查询结果，缓存时间为30分钟
        /// <summary>
        /// 输入 输出 方法名（接口路径）
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        public List<Good> GetGoods(GetGoodsReqeust request)
        {

            var key = $"GoodsCache_" + request.Keyword + "_" + request.CateId;

            //返回结果               key GoodsCache 当缓存存在时候 data会返回缓存的数据
            //第二次
            //输入 小米 结果是所有产品缓存 第一次请求（没有添加关键字）所以缓存的数据是没有关键字的搜索内容
            //需求 是缓存的结果要跟 查询条件一致
            if (cache.TryGetValue(key, out object data))
            {
                //如果有缓存 获取缓存数据
                return data as List<Good>;
            }
            else
            {
                //如果没有 查询数据库 保存缓存
                //实现逻辑 后端 Queryable待执行SQL
                var good = _db.Goods.AsQueryable();

                //关键字不为空的时候
                if (!string.IsNullOrEmpty(request.Keyword))
                {
                    //添加关键字查询条件
                    good = good.Where(x => x.Name.Contains(request.Keyword));
                }

                if (request.CateId > 0)
                {
                    good = good.Where(x => x.CateId == request.CateId);
                }
                //商品数据
                var goodList = good.ToList();
                //                                      缓存时间30分钟
                //key GoodsCache_小米_0
                cache.Set(key, goodList, TimeSpan.FromMinutes(30));
                return goodList;
            }
        }

        //编写商品详情接口，用户浏览商品详情时使用本地缓存保存登录用户浏览商品记录
        /// <summary>
        /// 输入 商品Id  输出 商品 
        /// </summary>
        [HttpGet]
        [Authorize]
        public Good GetGoodInfo(int goodId)
        {
            //查询数据
            var good = _db.Goods.FirstOrDefault(x => x.Id == goodId);

            //用户浏览商品详情时使用本地缓存保存登录用户浏览商品记录

            //使用缓存记录一个列表 （要缓存的数据）用户浏览过的商品 （不只一个 商品列表）

            //写入缓存方法 目前是一个固定值（每个用户的浏览记录 单独计算的）Key要跟用户关联
            //获取登录用户Id
            var userId = Response.HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.NameIdentifier).Value;
            //key
            var key = "MemberViewsGoodCache_" + userId;

            //以前的浏览商品记录 通过key
            var beforeCache = cache.Get<List<Good>>(key);

            //Object reference not set to an instance of an object 对象为null 不能去.使用
            ////以前的浏览商品记录 + 现在这次的商品
            if (beforeCache == null)
            {
                //创建一个空列表 不是null 空：数量为0的列表
                beforeCache = new List<Good>();
            }
            beforeCache.Add(good);

            var goodList = beforeCache;
            cache.Set(key, goodList);

            //返回结果
            return good;
        }
    }
}